/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.dusa.switco;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.w3c.dom.*;
import javax.xml.parsers.*;

/**
 * Singleton class reading task and precedence definitions from an xml file. See
 * in the project included xml files for the structure definition. Trees are included 
 * in the xml file only for purpose of testing. Some other information as well.
 * @author vdusa
 */
public class ScheduleReader {
    /**
     * ScheduleModel is a class holding the read file. See the function
     * {@link #read(java.lang.String)} for more information.
     */
    public final class ScheduleModel{
        public Map<Long, TreeHolder> trees = new HashMap<Long, TreeHolder>();
        public Map<Long, TaskHolder> tasks = new HashMap<Long, TaskHolder>();
        public List<PrecedenceHolder> precedences = new ArrayList<PrecedenceHolder>();
    }
    
    /**
     * Represents a tree. Trees are included in the xml file only for purpose of
     * testing. 
     */
    public final class TreeHolder{
        public Long id;
        public Double start;
    }
    
    /**
     * Represents task
     */
    public final class TaskHolder{
        public Long id;
        public String costFunction;
        public Double processingTime;
        public Double treeShift;
        public Long treeId;
    }
    
    /**
     * Represents precedence
     */
    public final class PrecedenceHolder{
        public Long predecessor;
        public Long successor;
        public Double maxDistance;
        public Double minDistance;
        public Long treeIdReg;
        public Long treeIdRope;
    }    

    private static ScheduleReader instance;

    private ScheduleReader(){};

    /**
     * @return singleton instance of ScheduleReader.
     */
    public static ScheduleReader getInstance(){
        if(instance == null){
            instance = new ScheduleReader();
        }
        return instance;
    }

    /**
     * Reads the given xml file
     * @param fileName to be read
     * @return {@link ScheduleModel} representing the read file.
     */
    ScheduleModel read(String fileName) {
        try {
            DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            Document doc = builder.parse(fileName);
            ScheduleModel model = new ScheduleModel();
            Map<Long, Task> tasks = new HashMap<Long, Task>();

            Element schedule = doc.getDocumentElement();
            NodeList treeNodes = schedule.getElementsByTagName("tree");
            NodeList taskNodes = schedule.getElementsByTagName("task");
            NodeList precedenceNodes = schedule.getElementsByTagName("precedence");            

            for (int t=0; t<treeNodes.getLength(); t++){
                Node treeNode = treeNodes.item(t);
                if (treeNode.getNodeType() == Node.ELEMENT_NODE){
                    TreeHolder treeHolder = new TreeHolder();
                    Element treeElement = (Element) treeNode;
                    treeHolder.id = Long.parseLong(getTagValue("id", treeElement));
                    treeHolder.start = Double.parseDouble(getTagValue("start", treeElement));
                    
                    model.trees.put(treeHolder.id, treeHolder);
                }
            }            
            
            for (int t = 0; t < taskNodes.getLength(); t++) {
                Node taskNode = taskNodes.item(t);
                if (taskNode.getNodeType() == Node.ELEMENT_NODE) {
                    TaskHolder taskHolder = new TaskHolder();
                    Element taskElement = (Element) taskNode;
                    taskHolder.id = Long.parseLong(getTagValue("id", taskElement));
                    taskHolder.costFunction = getTagValue("costfunction", taskElement);
                    taskHolder.processingTime = Double.parseDouble(getTagValue("processingtime", taskElement));
                    taskHolder.treeId = getTagValue("treeid", taskElement) != null ? Long.parseLong(getTagValue("treeid", taskElement)) : 0;
                    taskHolder.treeShift = getTagValue("treeshift", taskElement) != null ? Double.parseDouble(getTagValue("treeshift", taskElement)) : Double.NaN;

                    model.tasks.put(taskHolder.id, taskHolder);
                }
            }

            for (int p = 0; p < precedenceNodes.getLength(); p++) {
                Node precedenceNode = precedenceNodes.item(p);
                if (precedenceNode.getNodeType() == Node.ELEMENT_NODE) {
                    PrecedenceHolder precedenceHolder = new PrecedenceHolder();
                    Element precedenceElement = (Element) precedenceNode;
                    precedenceHolder.predecessor = Long.parseLong(getTagValue("predecessor", precedenceElement));
                    precedenceHolder.successor = Long.parseLong(getTagValue("successor", precedenceElement));
                    precedenceHolder.maxDistance = getTagValue("maxdistance", precedenceElement) != null ? Double.parseDouble(getTagValue("maxdistance", precedenceElement)) : Double.NaN;
                    precedenceHolder.minDistance = getTagValue("mindistance", precedenceElement) != null ? Double.parseDouble(getTagValue("mindistance", precedenceElement)) : Double.NaN;
                    precedenceHolder.treeIdReg = getTagValue("treeidreg", precedenceElement) != null ? Long.parseLong(getTagValue("treeidreg", precedenceElement)) : 0;
                    precedenceHolder.treeIdRope = getTagValue("treeidrope", precedenceElement) != null ? Long.parseLong(getTagValue("treeidrope", precedenceElement)) : 0;

                    model.precedences.add(precedenceHolder);
                }
            }           

            return model;
        } catch (Exception ex) {
            Logger.getLogger(ScheduleReader.class.getName()).log(Level.SEVERE, null, ex);
        }

        return null;
    }

    private static String getTagValue(String sTag, Element eElement) {
        try{
            NodeList nlList = eElement.getElementsByTagName(sTag).item(0).getChildNodes();

            Node nValue = (Node) nlList.item(0);

            if (nValue == null) {
                return null;
            } else {
                return nValue.getNodeValue();
            }
        } catch(Exception e){
            return null;
        }
    }
}
